﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Drawing;
using System.Reflection;
using System.Resources;
using System.Windows.Forms;
using Microsoft.VisualBasic;
using Microsoft.VisualBasic.ApplicationServices;
using Microsoft.VisualBasic.CompilerServices;

namespace VisualBasicRuntimeTest;

internal static class Program
{
    [STAThread]
    public static void Main(string[] args)
    {
        try
        {
            if (args.Length != 1)
            {
                throw new ArgumentException(message: null, nameof(args));
            }

            switch (args[0])
            {
                case "Interaction.InputBox":
                    Interaction_InputBox(useVbHost: false);
                    break;
                case "Interaction.InputBox_VbHost":
                    Interaction_InputBox(useVbHost: true);
                    break;
                case "Interaction.MsgBox":
                    Interaction_MsgBox(useVbHost: false);
                    break;
                case "Interaction.MsgBox_VbHost":
                    Interaction_MsgBox(useVbHost: true);
                    break;
                case "WindowsFormsApplicationBase.Run":
                    WindowsFormsApplicationBase_Run(isSingleInstance: false, isFirstInstance: true);
                    break;
                case "WindowsFormsApplicationBase.RunSingleInstance0":
                    WindowsFormsApplicationBase_Run(isSingleInstance: true, isFirstInstance: true);
                    break;
                case "WindowsFormsApplicationBase.RunSingleInstance1":
                    WindowsFormsApplicationBase_Run(isSingleInstance: true, isFirstInstance: false);
                    break;
                case "ProgressDialog.ShowProgressDialog":
                    ProgressDialog_ShowProgressDialog();
                    break;
                case "VBInputBox.ShowDialog":
                    VBInputBox_ShowDialog();
                    break;
                default:
                    throw new ArgumentException(message: null, nameof(args));
            }
        }
        catch (Exception)
        {
            Environment.Exit(2);
        }
    }

    private static void Interaction_InputBox(bool useVbHost)
    {
        VbHost host = useVbHost ? new VbHost() : null;
        HostServices.VBHost = host;
        Interaction.InputBox(Prompt: "Prompt", Title: "Title");
    }

    private static void Interaction_MsgBox(bool useVbHost)
    {
        VbHost host = useVbHost ? new VbHost() : null;
        HostServices.VBHost = host;
        Interaction.MsgBox(Prompt: "Message", Buttons: MsgBoxStyle.OkCancel, Title: "Title");
    }

    private static void WindowsFormsApplicationBase_Run(bool isSingleInstance, bool isFirstInstance)
    {
        Form mainForm = new();
        WindowsApplication application = new(mainForm, isSingleInstance);

        bool valid = false;
        bool loaded = false;
        bool startUpNextInstance = false;

        application.StartupNextInstance += (object sender, StartupNextInstanceEventArgs e) =>
        {
            startUpNextInstance = true;
            if (!isFirstInstance)
            {
                mainForm.Close();
            }
        };

        mainForm.Load += (object sender, EventArgs e) =>
        {
            loaded = true;
            var forms = application.OpenForms;
            valid = forms.Count == 1
                && forms[0] == mainForm
                && application.ApplicationContext.MainForm == mainForm;
            if (!valid)
            {
                mainForm.Close();
            }
        };

        application.Run([]);

        if (startUpNextInstance)
        {
            throw new InvalidOperationException();
        }

        if (!loaded && !isFirstInstance)
        {
            throw new InvalidOperationException();
        }

        if (!valid)
        {
            throw new InvalidOperationException();
        }
    }

    private static void ProgressDialog_ShowProgressDialog()
    {
        Type dialogType = typeof(ApplicationBase).Assembly.GetType("Microsoft.VisualBasic.MyServices.Internal.ProgressDialog");
        var dialog = (Form)Activator.CreateInstance(dialogType, nonPublic: true);

        ResourceManager resources = new(dialogType);
        var expectedValue = (Point)resources.GetObject("ProgressBarWork.Location");
        if (expectedValue == new Point(0, 0))
        {
            throw new InvalidOperationException();
        }

        PropertyInfo controlProperty = dialogType.GetProperty("ProgressBarWork", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
        var control = (Control)controlProperty.GetValue(dialog);
        Point actualValue = control.Location;
        if (actualValue != expectedValue)
        {
            throw new InvalidOperationException();
        }

        MethodInfo showMethod = dialogType.GetMethod("ShowProgressDialog");
        showMethod.Invoke(dialog, null);
    }

    private static void VBInputBox_ShowDialog()
    {
        Type formType = typeof(ApplicationBase).Assembly.GetType("Microsoft.VisualBasic.CompilerServices.VBInputBox");
        var form = (Form)Activator.CreateInstance(formType, nonPublic: true);

        ResourceManager resources = new(formType);
        var expectedValue = (Point)resources.GetObject("TextBox.Location");
        if (expectedValue == new Point(0, 0))
        {
            throw new InvalidOperationException();
        }

        FieldInfo controlField = formType.GetField("TextBox", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
        var control = (Control)controlField.GetValue(form);
        Point actualValue = control.Location;
        if (actualValue != expectedValue)
        {
            throw new InvalidOperationException();
        }

        form.ShowDialog();
    }

    private sealed class WindowsApplication : WindowsFormsApplicationBase
    {
        internal WindowsApplication(Form mainForm, bool isSingleInstance)
        {
            MainForm = mainForm;
            IsSingleInstance = isSingleInstance;
        }
    }

    private static string GetUniqueName() => Guid.NewGuid().ToString("D");

    private sealed class VbHost : IVbHost
    {
        private readonly string _title = GetUniqueName();

        public IWin32Window GetParentWindow() => null;

        public string GetWindowTitle() => _title;
    }
}
